package demo;

import static demo.utils.ColorUtils.*;
import ru.nsu.fit.iconveyor.conveyor.node.InnerNode;
import ru.nsu.fit.iconveyor.conveyor.node.NodeConnection;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class BlurNode extends InnerNode {
    public static final String INPUT_IMAGE = "input_image";
    public static final String OUTPUT_IMAGE = "output_image";

    private final NodeConnection<Image> inputImage;
    private final NodeConnection<Image> outputImage;

    private static final double [][] BLUR_MATRIX =
            {
                    { 1.0/9.0 , 1.0/9.0, 1.0/9.0 },
                    { 1.0/9.0 , 1.0/9.0, 1.0/9.0 },
                    { 1.0/9.0 , 1.0/9.0, 1.0/9.0 },
            };

    public BlurNode(String id) {
        super(id);

        inputImage = new NodeConnection<Image>(Image.class, NodeConnection.Type.INPUT, INPUT_IMAGE);
        outputImage = new NodeConnection<Image>(Image.class, NodeConnection.Type.OUTPUT, OUTPUT_IMAGE);

        addNodeConnection(inputImage);
        addNodeConnection(outputImage);
    }

    @Override
    public int getOutputToInputRatio() {
        return 1;
    }

    @Override
    public Map<String, Object> preExecute(Map<String, Object> availableInputBundles, Properties prop) {
        if(availableInputBundles.containsKey(INPUT_IMAGE)) {
            return availableInputBundles;
        }
        return null;
    }

    @Override
    public Map<String, Object> execute(Map<String, Object> inputBundles, Properties prop) {
        final Image inputImage = (Image)inputBundles.get(INPUT_IMAGE);
        final int [][] inputBitmap = inputImage.getBitmap();
        final int width = inputBitmap.length;
        final int height = inputBitmap[0].length;
        final int [][] outputBitmap = new int[width][height];
        for(int i = 0 ; i < width ; ++i) {
            for(int j = 0 ; j < height ; ++j) {
                if(i <= 0 || i >= width - 1 || j <= 0 || j >= height - 1) {
                    outputBitmap[i][j] = inputBitmap[i][j];
                } else {
                    double outputR = 0.0;
                    double outputG = 0.0;
                    double outputB = 0.0;

                    for(int k = -1 ; k <= 1 ; ++k) {
                        for(int t = -1 ; t <= 1 ; ++t) {
                            final int inputRGB = inputBitmap[i+k][j+t];
                            final int r = getR(inputRGB);
                            final int g = getG(inputRGB);
                            final int b = getB(inputRGB);

                            final double coeff = BLUR_MATRIX[1+k][1+t];

                            outputR += coeff * r;
                            outputG += coeff * g;
                            outputB += coeff * b;
                        }
                    }

                    outputBitmap[i][j] = toRGB(roundRoByte(outputR), roundRoByte(outputG), roundRoByte(outputB));
                }
            }

        }
        final Image outputImage = new Image();
        outputImage.setBitmap(outputBitmap);
        outputImage.setName(inputImage.getName());
        return new HashMap<String, Object>() {
            {
                put(OUTPUT_IMAGE, outputImage);
            }
        };
    }

    private static int roundRoByte(int i) {
        if(i < 0) {
           return 0;
        }
        if(i > 255) {
            return 255;
        }
        return i;
    }

    private static int roundRoByte(double d) {
        return roundRoByte((int)(d + 0.5));
    }

}
